package com.jsh.erp.service;

import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel;
import com.google.common.util.concurrent.ListenableFuture;
import com.jsh.erp.datasource.entities.*;
import com.jsh.erp.datasource.mappers.*;
import com.jsh.erp.utils.QdrantDocumentUtils;
import com.jsh.erp.utils.UUIDUtil;
import com.jsh.erp.utils.VectorStrUtil;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.WithVectorsSelectorFactory;
import io.qdrant.client.grpc.Collections;
import io.qdrant.client.grpc.Points;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.ai.document.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorsFactory.vectors;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;

/**
 * embeddingStoreMap内元素的初始化在userService.login的过程中完成
 */
@Service
public class KnowledgeBaseService {
    @Resource
    private QdrantClient qdrantClient;
    @Value("${spring.vectorstore.qdrant.collection-name}")
    private String qdrantCollectionName;
    @Resource
    private OrganizationMapper organizationMapper;
    @Resource
    private DashScopeEmbeddingModel dashScopeEmbeddingModel;
    @Resource(name = "commonUserService")
    private CommonUserService commonUserService;
    @Resource
    private PersonMapperEx personMapperEx;
    @Resource
    private MaterialMapper materialMapper;
    @Resource
    private MaterialCategoryMapper materialCategoryMapper;
    @Resource
    private UnitMapper unitMapper;
    @Resource
    private DepotMapper depotMapper;
    @Resource
    private MaterialCurrentStockMapper materialCurrentStockMapper;
    @Autowired
    private AccountService accountService;
    @Autowired
    private SupplierService supplierService;
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountItemMapper accountItemMapper;
    @Autowired
    private AccountHeadMapper accountHeadMapper;
    @Autowired
    private PersonMapper personMapper;

    @PostConstruct
    public void init() {
        System.out.println(this);
    }


    public int insertToQdrant(Object obj, String vectorId) throws Exception {
        String documentStr = getVectorStr(obj);
        Long tenantId = commonUserService.getCurrentUser().getTenantId();
        // 1. 使用嵌入模型将文本转换为向量（Embedding）
        float[] embed = dashScopeEmbeddingModel.embed(documentStr);
        List<Float> vector = new ArrayList<>();
        for (int i = 0; i < embed.length; i++) {
            vector.add(embed[i]);
        }
        // 4. 构建Qdrant的数据点（PointStruct）
        Points.PointId pointId = Points.PointId.newBuilder().setUuid(vectorId).build();
        Points.PointStruct point = Points.PointStruct.newBuilder()
                .setId(pointId) // 设置此点的唯一ID
                .setVectors(vectors(vector)) // 设置多维向量
                .putAllPayload( // 设置此点的元数据（负载），是一个或多个键值对 Map
                        Map.of("tenant_id", value(tenantId.toString()),
                                "text_segment", value(documentStr),
                                "time", value(new Date().toString())
                        )
                )
                .build();// 构建一个点

        com.google.common.util.concurrent.ListenableFuture<Points.UpdateResult> updateResultListenableFuture = qdrantClient.upsertAsync(qdrantCollectionName, List.of(point));
        return updateResultListenableFuture.get().getStatus().getNumber();
    }

    public String queryFromQdrant(String question) throws Exception {
        float[] queryEmbedding = null;
        try {
            queryEmbedding = dashScopeEmbeddingModel.embed(question);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        List<Float> queryEmbeddingList = new ArrayList<>();
        for (int i = 0; i < queryEmbedding.length; i++) {
            queryEmbeddingList.add(queryEmbedding[i]);
        }
        Long tenantId = commonUserService.getCurrentUser().getTenantId();
        // 2. 查询向量数据库
        Points.Filter filter = Points.Filter.newBuilder().addMust(matchKeyword("tenant_id", tenantId.toString())).build();
        com.google.common.util.concurrent.ListenableFuture<List<Points.ScoredPoint>> listListenableFuture = qdrantClient.searchAsync(Points.SearchPoints.newBuilder()
                .setCollectionName(qdrantCollectionName)
                .addAllVector(queryEmbeddingList)
                .setLimit(1000)
                //要求 Qdrant 在返回的结果中必须包含每个点的 Payload（负载）
                .setWithPayload(enable(true))
                //要求 Qdrant 在返回的结果中也包含向量本身,一般不需要开启，除非需要操作向量进行计算
                .setWithVectors(WithVectorsSelectorFactory.enable(false))
                .setFilter(filter)
                .build());
        List<Points.ScoredPoint> results = listListenableFuture.get();
        Stream<Points.ScoredPoint> stream = results.stream();

        Stream<Document> textSegment = stream.map(point -> QdrantDocumentUtils.toDocument(point, "text_segment"));
        List<Document> collect = textSegment.collect(Collectors.toList());

        String context = collect.stream()
                .map(Document::getText)
                .filter(Objects::nonNull)
                .collect(Collectors.joining("\n\n---\n\n"));
        return context;
    }

    public int deleteFromQdrantPointIds(List<String> vectorIds) throws Exception {
        ArrayList<Points.PointId> ids = new ArrayList<>();
        for (String vectorId : vectorIds) {
            ids.add(Points.PointId.newBuilder().setUuid(vectorId).build());
        }
        ListenableFuture<Points.UpdateResult> updateResultListenableFuture = qdrantClient.deleteAsync(qdrantCollectionName, ids);
        return updateResultListenableFuture.get().getStatus().getNumber();
    }

    public void deleteFromQdrantPointId(String vectorId) throws Exception {
        ArrayList<String> ids = new ArrayList<>();
        ids.add(vectorId);
        deleteFromQdrantPointIds(ids);
    }

    public String getVectorStr(Object obj) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
        String simpleName = obj.getClass().getSimpleName();
        map.put("entryType", simpleName);
        switch (simpleName) {
            case "AccountHead": //财务单据
                AccountHead accountHead = (AccountHead) obj;
                Long organId = accountHead.getOrganId();
                map.put("type", accountHead.getType());
                if ("收入".equals(accountHead.getType())
                        || "支出".equals(accountHead.getType())
                        || "收款".equals(accountHead.getType())
                        || "付款".equals(accountHead.getType())
                ) {
                    if (organId != null) {
                        map.put("organ", supplierService.getSupplier(organId).getSupplier());
                    }
                } else if ("转账".equals(accountHead.getType())){
                    Account account = accountMapper.selectByPrimaryKey(accountHead.getAccountId());
                    map.put("outAccount",account.getName());

                    AccountHeadExample accountHeadExample = new AccountHeadExample();
                    AccountHeadExample.Criteria criteria = accountHeadExample.createCriteria();
                    criteria.andBillTimeEqualTo(accountHead.getBillTime())
                            .andTotalPriceEqualTo(accountHead.getTotalPrice())
                            .andAccountIdEqualTo(accountHead.getAccountId());
                    accountHead = accountHeadMapper.selectByExample(accountHeadExample).get(0);
                    Long accountId = accountHead.getId();

                    AccountItemExample accountItemExample = new AccountItemExample();
                    AccountItemExample.Criteria accountItemCriteria = accountItemExample.createCriteria();
                    accountItemCriteria.andHeaderIdEqualTo(accountId);
                    AccountItem accountItem = accountItemMapper.selectByExample(accountItemExample).get(0);
                    Long inAccountId = accountItem.getAccountId();
                    Long handsPersonId = accountHead.getHandsPersonId();
                    if (handsPersonId != null) {
                        map.put("person",personMapper.selectByPrimaryKey(handsPersonId).getName());
                    }
                    map.put("inAccount", accountMapper.selectByPrimaryKey(inAccountId).getName());
                    map.put("remark1",accountItem.getRemark());
                    map.put("totalPrice1",accountItem.getEachAmount());

                } else {

                    Long handsPersonId = accountHead.getHandsPersonId();
                    if (handsPersonId != null) {
                        map.put("handsPerson", personMapperEx.getNameByPrimaryKey(handsPersonId));
                    }
                    map.put("creator", commonUserService.getCurrentUser().getUsername());//操作员
                    map.put("discountMoney", String.valueOf(accountHead.getDiscountMoney()));//优惠金额
                    map.put("remark", accountHead.getRemark());//备注
                    if (organId != null) {
                        map.put("organ", organizationMapper.getNameByPrimaryKey(organId));
                    }
                }

                Long accountId = accountHead.getAccountId();
                if (accountId != null) {
                    //账户名
                    map.put("accountName", accountService.getAccountNameById(accountId));
                }

                map.put("changeAmount", String.valueOf(accountHead.getChangeAmount()));
                map.put("totalPrice", String.valueOf(accountHead.getTotalPrice()));//合计金额
                map.put("billTime", String.valueOf(accountHead.getBillTime()));//单据时间
                map.put("billNo", accountHead.getBillNo());
                break;
            case "InOutItem": //收支项目"
                InOutItem inOutItem = (InOutItem) obj;
                map.put("name", inOutItem.getName());
                map.put("type", inOutItem.getType());
                map.put("remark", inOutItem.getRemark());
                break;
            case "Depot": //仓库信息
                Depot depot = (Depot) obj;
                map.put("depot_nema", depot.getName());
                map.put("address", depot.getAddress());
                break;
            case "Supplier"://客户/供应商
                Supplier supplier = (Supplier) obj;
                map.put("type", supplier.getType());//类型：客户/供应商
                map.put("supplier_name", supplier.getSupplier());
                map.put("contacts", supplier.getContacts());//联系人
                map.put("phone_num", supplier.getPhoneNum());//联系电话
                map.put("telephone", supplier.getTelephone());//联系人手机号
                map.put("address", supplier.getAddress());//地址
                map.put("description", supplier.getDescription());//备注
                map.put("all_need_get", String.valueOf(supplier.getAllNeedGet()));//累计应收
                map.put("all_need_pay", String.valueOf(supplier.getAllNeedPay()));//累计应付
                map.put("account_number", supplier.getAccountNumber());//银行账号
                map.put("bank_name", supplier.getBankName());//开户行
                map.put("tax_num", supplier.getTaxNum());//纳税人识别号
                break;
            case "Material":
                Material material = (Material) obj;
                map.put("name", material.getName());
                map.put("type", "产品");
                MaterialCategory m = materialCategoryMapper.selectByPrimaryKey(material.getCategoryId());
                if (m != null) {
                    map.put("category", m.getName());//产品类型
                }
                map.put("model", material.getModel());//型号
                map.put("standard", material.getStandard());//规格
                map.put("color", material.getColor());//颜色
                map.put("unit", material.getUnit());//单位
                Long unitId = material.getUnitId();
                if (unitId != null) {
                    map.put("unit_id", unitMapper.selectByPrimaryKey(unitId).getName());//计量单位
                }
                map.put("mfrs", material.getMfrs());//制造商
                map.put("remark", material.getRemark());
                break;
            case "MaterialCategory":
                MaterialCategory materialCategory = (MaterialCategory) obj;
                map.put("name", materialCategory.getName());
                map.put("remark", materialCategory.getRemark());
                String s = "无上级类别";
                if (materialCategory.getParentId() != null) {
                    s = materialCategoryMapper.selectNameByPrimaryKey(materialCategory.getParentId());
                }
                map.put("partner", s);
                map.put("createTime", materialCategory.getCreateTime().toString());
                break;
            case "MaterialCurrentStock":
                MaterialCurrentStock materialCurrentStock = (MaterialCurrentStock) obj;
                map.put("name", materialMapper.selectByPrimaryKey(materialCurrentStock.getMaterialId()).getName());//商品名称
                map.put("depot", depotMapper.selectNameByPrimaryKey(materialCurrentStock.getDepotId()));//存放仓库
                map.put("number", materialCurrentStock.getCurrentNumber().toString());//存放数量
                break;
            case "Person":
                Person person = (Person)obj;
                map.put("type",person.getType());
                map.put("name",person.getName());
                break;
            case "HashMap":
                Map entryMap = (Map) obj;
                map.put("entryType", (entryMap.get("subType")));
                if ("其它".equals(entryMap.get("actualType"))) {
                    map.put("actualType", "其它" + entryMap.get("entryType"));
                } else if ("调拨".equals(entryMap.get("actualType"))) {
                    map.put("actualType", "调拨出库");
                } else {
                    map.put("actualType", "销售".equals(entryMap.get("actualType")) ? "销售出库" : entryMap.get("actualType"));
                    map.put("totalAmount", entryMap.get("totalAmount"));
                    map.put("discount", entryMap.get("discount"));
                    map.put("discountAmount", entryMap.get("discountAmount"));
                    map.put("amount", entryMap.get("amount"));
                }
                map.put("defaultNumber", entryMap.get("defaultNumber"));
                map.put("supplierName", entryMap.get("supplierName"));
                String accountName = (String) entryMap.get("accountName");
                String status = (String) entryMap.get("status");
                if (accountName != null) {
                    map.put("accountName", accountName);
                }
                List<DepotItem> depotItems = (List<DepotItem>) entryMap.get("depotItems");
                ArrayList<Map<String, Object>> inMaps = new ArrayList<>();
                for (DepotItem depotItem : depotItems) {
                    HashMap<String, Object> inMap = new HashMap<>();
                    if ("0".equals(status)) {
                        map.put("status", "未审核");
                    } else if ("1".equals(status)) {
                        map.put("status", "已审核");
                    } else if ("2".equals(status)) {
                        map.put("status", "已强制关单");
                    }
                    try {
                        inMap.put("materia", materialMapper.selectByPrimaryKey(depotItem.getMaterialId()).getName());//
                        inMap.put("materiaUnit", depotItem.getMaterialUnit());//
                        inMap.put("basicNumber", depotItem.getBasicNumber().toString());
                        Long depotId = depotItem.getDepotId();
                        if (depotId != null) {
                            inMap.put("depotName", depotMapper.selectByPrimaryKey(depotId).getName());
                        }
                        if ("调拨".equals(entryMap.get("actualType"))) {
                            Long anotherDepotId = depotItem.getAnotherDepotId();
                            if (anotherDepotId != null) {
                                inMap.put("anotherDepotName", depotMapper.selectByPrimaryKey(anotherDepotId).getName());
                            }
                        }
                        inMaps.add(inMap);
                    } catch (ClassCastException e) {
                        throw e;
                    }
                }
                map.put("inMaps", inMaps);
                break;
        }

        return VectorStrUtil.toVectorStr(map);
    }


    //租户+类型+time的uuid值
    public String getVectorId(String className) throws Exception {
        Long tenantId = commonUserService.getCurrentUser().getTenantId();
        return UUIDUtil.generateUuidFromString(tenantId + "_" + className.toUpperCase() + "_" + System.currentTimeMillis()).toString();
    }

    public void createCollection() {
        Collections.VectorParams vectorParams = Collections.VectorParams.newBuilder().setSize(Long.parseLong("768")).setDistanceValue(1).build();
        qdrantClient.createCollectionAsync(qdrantCollectionName, vectorParams);
    }

    public void updateCurrentStock(DepotItem depotItem) throws Exception {
        MaterialCurrentStockExample materialCurrentStockExample = new MaterialCurrentStockExample();
        MaterialCurrentStockExample.Criteria criteria = materialCurrentStockExample.createCriteria();
        criteria.andMaterialIdEqualTo(depotItem.getMaterialId());
        criteria.andTenantIdEqualTo(commonUserService.getCurrentUser().getTenantId());
        criteria.andDepotIdEqualTo(depotItem.getDepotId());
        List<MaterialCurrentStock> materialCurrentStocks = materialCurrentStockMapper.selectByExample(materialCurrentStockExample);
        for (MaterialCurrentStock materialCurrentStock : materialCurrentStocks) {
            insertToQdrant(materialCurrentStock, materialCurrentStock.getVectorId());
        }
    }
}
